From 5d03eca3fe6dec88bcdfabee20c6e902f89612eb Mon Sep 17 00:00:00 2001 From: robertl Date: Fri, 7 Feb 2003 07:27:20 +0000 Subject: [PATCH] Add filter infrastructure plus two filters for name and position. Thanx, Alex! --- gpsbabel/Makefile | 8 ++- gpsbabel/README | 45 +++++++++++++ gpsbabel/defs.h | 15 +++++ gpsbabel/filter_vecs.c | 102 ++++++++++++++++++++++++++++++ gpsbabel/gpx.c | 3 + gpsbabel/main.c | 19 ++++++ gpsbabel/position.c | 140 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 330 insertions(+), 2 deletions(-) create mode 100644 gpsbabel/filter_vecs.c create mode 100644 gpsbabel/position.c diff --git a/gpsbabel/Makefile b/gpsbabel/Makefile index 0898812e3..147ee0414 100644 --- a/gpsbabel/Makefile +++ b/gpsbabel/Makefile @@ -6,6 +6,8 @@ FMTS=magproto.o gpx.o geo.o gpsman.o mapsend.o mapsource.o \ psp.o mxf.o holux.o garmin.o ozi.o tmpro.o dna.o tpg.o gpsdrive.o \ xcsv.o xmapwpt.o gcdb.o +FILTERS=position.o duplicate.o + JEEPS=jeeps/gpsapp.o jeeps/gpscom.o \ jeeps/gpsmath.o jeeps/gpsmem.o \ jeeps/gpsprot.o jeeps/gpsread.o \ @@ -16,8 +18,8 @@ JEEPS=jeeps/gpsapp.o jeeps/gpscom.o \ COLDSYNC=coldsync/util.o coldsync/pdb.o -OBJS=main.o queue.o route.o waypt.o util.o vecs.o mkshort.o csv_util.o \ - $(COLDSYNC) $(GARMIN) $(JEEPS) $(FMTS) +OBJS=main.o queue.o route.o waypt.o filter_vecs.o util.o vecs.o mkshort.o csv_util.o \ + $(COLDSYNC) $(GARMIN) $(JEEPS) $(FMTS) $(FILTERS) .c.o: $(CC) -c $(CFLAGS) $< -o $@ @@ -94,6 +96,8 @@ tpg.o: tpg.c defs.h queue.h jeeps/gpsmath.h jeeps/gps.h jeeps/gpsport.h \ jeeps/gpsproj.h jeeps/gpsnmeafmt.h jeeps/gpsnmeaget.h util.o: util.c defs.h queue.h vecs.o: vecs.c defs.h queue.h +filter_vecs.o: filter_vecs.c defs.h queue.h +position.o:position.c defs.h waypt.o: waypt.c defs.h queue.h xcsv.o: xcsv.c defs.h queue.h csv_util.h xmapwpt.o: xmapwpt.c defs.h queue.h csv_util.h diff --git a/gpsbabel/README b/gpsbabel/README index 578eaaa94..1138b4e67 100644 --- a/gpsbabel/README +++ b/gpsbabel/README @@ -292,6 +292,51 @@ THE FORMATS http://vip.hyperusa.com/~dougs/geocachingdb/geocachingdb.htm +DATA FILTERS + + GPSBabel supports data filtering. Data filters are invoked from + the command line via the '-x' option. It should be noted that + data filters are invoked in the order they appear on the command + line and can be used in intermittently between several variations + of input and output functions. It should also be noted that + filtering data from different input types can sometimes produce + undesirable results due to differences in the native data formats. + + + POSITION + + The position filter is designed to remove points based on their + proximity to each other. Distances can be passed on the command + line by passing the distance=XXX option to the filter. Distance + options may be expressed in feet (distance=3f) or meters + (distance=1m). The default is zero feet, essentially a duplicate + position. + + For example: + + gpsbabel -i geo -f 1.loc -f 2.loc -x position,distance=1f \ + -o mapsend -F 3.wpt + + would remove multiple points that are within 1 foot of each other, + leaving just one. + + + DUPLICATE + + The duplicate filter is designed to remove duplicate points based + on their shortname (traditionally a waypoint's name on the GPS + receiver), and/or their location (to a precision of 6 decimals). + This filter supports two options, "shortname" and "location". + Generally, at least one of these options is REQUIRED. For example: + + gpsbabel -i gpx -f 1.gpx -f 2.gpx -x duplicate,location,shortname \ + -o gpx -F merged_with_no_dupes.gpx + + would remove points that have duplicate shortnames *AND* duplicate + locations. The result would be a GPX file that more than likely + contains only unique points and point data. + + COMMON USAGE Invocation was meant to be flexible. Unfortunately, that can diff --git a/gpsbabel/defs.h b/gpsbabel/defs.h index 69f9ec7a1..02b67f02b 100644 --- a/gpsbabel/defs.h +++ b/gpsbabel/defs.h @@ -174,6 +174,10 @@ typedef void (*ff_read) (void); typedef void (*ff_write) (void); char * get_option(const char *iarglist, const char *argname); +typedef void (*filter_init) (char const *); +typedef void (*filter_process) (void); +typedef void (*filter_deinit) (void); + void fprintdms(FILE *, const coord *, int); typedef void (*waypt_cb) (const waypoint *); @@ -218,6 +222,12 @@ typedef struct ff_vecs { arglist_t *args; } ff_vecs_t; +typedef struct filter_vecs { + filter_init f_init; + filter_process f_process; + filter_deinit f_deinit; +} filter_vecs_t; + void waypt_init(void); void route_init(void); void waypt_disp(const waypoint *); @@ -226,11 +236,16 @@ void fatal(const char *, ...) __attribute__ ((__format__ (__printf__, 1, 2))) #endif ; + ff_vecs_t *find_vec(char *, char **); void disp_vecs(void); void disp_formats(void); void printposn(const coord *c, int is_lat); +filter_vecs_t * find_filter_vec(char *, char **); +void disp_filters(void); +void disp_filter_vecs(void); + void *xcalloc(size_t nmemb, size_t size); void *xmalloc(size_t size); char *xstrdup(const char *s); diff --git a/gpsbabel/filter_vecs.c b/gpsbabel/filter_vecs.c new file mode 100644 index 000000000..f4c0c2ed4 --- /dev/null +++ b/gpsbabel/filter_vecs.c @@ -0,0 +1,102 @@ +/* + Describe vectors containing filter operations. + + Copyright (C) 2002 Robert Lipe, robertlipe@usa.net + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA + + */ + +#include +#include "defs.h" + +typedef struct { + filter_vecs_t *vec; + const char *name; + const char *desc; +} fl_vecs_t; + +extern filter_vecs_t position_vecs; +extern filter_vecs_t duplicate_vecs; + +static +fl_vecs_t filter_vec_list[] = { + { + &position_vecs, + "position", + "Remove Points Within Distance", + }, + { + &duplicate_vecs, + "duplicate", + "Remove Duplicates", + }, + { + NULL, + NULL, + NULL + } +}; + +filter_vecs_t * +find_filter_vec(char *const vecname, char **opts) +{ + fl_vecs_t *vec = filter_vec_list; + char *v = xstrdup(vecname); + char *svecname = strtok(v, ","); + + while (vec->vec) { + if (strcmp(svecname, vec->name) == 0) { + char * res = strchr(vecname, ','); + if (res) + *opts = strchr(vecname, ',')+1; + else + *opts = NULL; + free(v); + return vec->vec; + } + vec++; + } + free(v); + return NULL; +} + +/* + * Display the available formats in a format that's easy for humans to + * parse for help on available command line options. + */ +void +disp_filter_vecs(void) +{ + fl_vecs_t *vec; + for (vec = filter_vec_list; vec->vec; vec++) { + printf(" %-20.20s %-50.50s\n", + vec->name, vec->desc); + } +} + +/* + * Display the available formats in a format that's easy to machine + * parse. Typically invoked by programs like graphical wrappers to + * determine what formats are supported. + */ +void +disp_filters(void) +{ + fl_vecs_t *vec; + for (vec = filter_vec_list; vec->vec; vec++) { + printf("%s\t%s\n", vec->name, vec->desc); + } +} diff --git a/gpsbabel/gpx.c b/gpsbabel/gpx.c index 46b3ac25b..c77361e02 100644 --- a/gpsbabel/gpx.c +++ b/gpsbabel/gpx.c @@ -625,6 +625,9 @@ gpx_write_time(const time_t timep) { struct tm *tm = gmtime(&timep); + if (!tm) + return; + fprintf(ofd, "\n", tm->tm_year+1900, tm->tm_mon+1, diff --git a/gpsbabel/main.c b/gpsbabel/main.c index 116d4fc3c..35dd1c293 100644 --- a/gpsbabel/main.c +++ b/gpsbabel/main.c @@ -55,6 +55,8 @@ usage(const char *pname) ); disp_vecs(); + printf("\nSupported data filters:\n"); + disp_filter_vecs(); } @@ -66,10 +68,12 @@ main(int argc, char *argv[]) int argn; ff_vecs_t *ivecs = NULL; ff_vecs_t *ovecs = NULL; + filter_vecs_t *fvecs = NULL; char *fname = NULL; char *ofname = NULL; char *ivec_opts = NULL; char *ovec_opts = NULL; + char *fvec_opts = NULL; global_opts.objective = wptdata; @@ -140,6 +144,18 @@ main(int argc, char *argv[]) case 'r': global_opts.objective = rtedata; break; + case 'x': + optarg = argv[argn][2] + ? argv[argn]+2 : argv[++argn]; + + fvecs = find_filter_vec(optarg, &fvec_opts); + + if (fvecs) { + fvecs->f_init(fvec_opts); + fvecs->f_process(); + fvecs->f_deinit(); + } + break; case 'D': optarg = argv[argn][2] ? argv[argn]+2 : argv[++argn]; @@ -148,6 +164,9 @@ main(int argc, char *argv[]) case '^': disp_formats(); exit(0); + case '%': + disp_filters(); + exit(0); case 'h': case '?': usage(argv[0]); diff --git a/gpsbabel/position.c b/gpsbabel/position.c new file mode 100644 index 000000000..ef33137dd --- /dev/null +++ b/gpsbabel/position.c @@ -0,0 +1,140 @@ +/* + Distance Between Points Filter + + Copyright (C) 2002 Robert Lipe, robertlipe@usa.net + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA + + */ +#include +#include +#include "defs.h" + +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif + +extern queue waypt_head; + +static double pos_dist; + +static double +gc_distance(double lat1, double lon1, double lat2, double lon2) +{ + double rlat1, rlat2, rlon1, rlon2; + + /* convert to radians */ + rlat1 = (lat1 * M_PI) / 180.0; + rlon1 = (lon1 * M_PI) / 180.0; + rlat2 = (lat2 * M_PI) / 180.0; + rlon2 = (lon2 * M_PI) / 180.0; + + return (acos((sin(rlat1) * sin(rlat2)) + + (cos(rlat1) * cos(rlat2) * cos(rlon1 - rlon2)))); +} + +static int +position_comp(const void * a, const void * b) +{ + const waypoint *x1 = *(waypoint **)a; + const waypoint *x2 = *(waypoint **)b; + double latdiff, londiff, max; + + /* + * this compare makes the assumption that things will fall into + * order by declaring their biggest single axial difference. + * It is much less math than distance and bearing from some random + * point. + */ + + londiff = (x1->position.longitude.degrees - + x2->position.longitude.degrees) * 1000000.0; + latdiff = (x1->position.latitude.degrees - + x2->position.latitude.degrees) * 1000000.0; + + max = fabs(londiff) >= fabs(latdiff) ? floor(londiff) : floor(latdiff); + + if (max < 0) + return (-1); + else if (max > 0) + return (1); + + return(0); +} + +void +position_process(void) +{ + queue * elem, * tmp; + waypoint ** comp; + double dist; + int i, wc; + + wc = waypt_count(); + + comp = xcalloc(wc, sizeof(*comp)); + + i = 0; + + QUEUE_FOR_EACH(&waypt_head, elem, tmp) { + comp[i] = (waypoint *)elem; + i++; + } + + qsort(comp, wc, sizeof(waypoint *), position_comp); + + for (i = 0 ; i < (wc - 1) ; i++) { + dist = gc_distance(comp[i]->position.latitude.degrees, + comp[i]->position.longitude.degrees, + comp[i+1]->position.latitude.degrees, + comp[i+1]->position.longitude.degrees); + + /* convert radians to integer feet */ + dist = (int)((((dist * 180.0 * 60.0) / M_PI) / 1.1516) * 5280.0); + + if (dist <= pos_dist) + waypt_del(comp[i]); + } + + if (comp) + free (comp); +} + +void +position_init(const char *args) { + char *fm; + const char *p; + + p = get_option(args, "distance"); + + if (p) { + pos_dist = strtod(p, &fm); + + if ((*fm == 'm') || (*fm == 'M')) { + /* distance is meters */ + pos_dist *= 3.2802; + } + } +} + +void +position_deinit(void) { +} + +filter_vecs_t position_vecs = { + position_init, + position_process, + position_deinit +}; -- 2.30.2